/*
 * Copyright (C) 2009,2010,2011  Paulo Cesar Pereira de Andrade.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Authors:
 *	Paulo Cesar Pereira de Andrade
 */

#include "exl.h"
#if JITVM && DISASSEMBLER
#  include "ejit.h"
#endif

#include <stdio.h>

#if JITVM && DISASSEMBLER
#  include <dis-asm.h>
#endif

#define dputc(ch)		eputc(std_output, ch)
#define dputs(object, length)	ewrite(std_output, object, length)

#define print_int(value)	eprint_int(std_output, format, value)
#define print_flt(value)	eprint_flt(std_output, format, value)
#define print_str(value)	eprint_str(std_output, format, value)
#define print_mpz(value)	eprint_mpz(std_output, format, value)
#define print_mpq(value)	eprint_mpq(std_output, format, value)
#define print_mpr(value)	eprint_mpr(std_output, format, value)
#define print_cdd(value)	eprint_cdd(std_output, format, value)
#define print_cqq(value)	eprint_cqq(std_output, format, value)
#define print_mpc(value)	eprint_mpc(std_output, format, value)
#define print_ptr(value)	eprint_ptr(std_output, format, value)
#define print_sym(value)	dputs(value->name->v.obj, value->name->length)
#define print_obj(value)	write_object(value, format)
#define print_rec(value)	print_sym(value->name)
#define print_tag(value)	write_tag(value, format)
#define print_ast(value)	write_ast_node(value, format)
#define print_vi8(vec)		write_i8_vector(vec->v.obj, vec->length, format)
#define print_vi16(vec)		write_i16_vector(vec->v.obj, vec->length, format)
#define print_vu16(vec)		write_u16_vector(vec->v.obj, vec->length, format)
#define print_vi32(vec)		write_i32_vector(vec->v.obj, vec->length, format)
#define print_vu32(vec)		write_u32_vector(vec->v.obj, vec->length, format)
#define print_vi64(vec)		write_i64_vector(vec->v.obj, vec->length, format)
#define print_vu64(vec)		write_u64_vector(vec->v.obj, vec->length, format)
#define print_vf32(vec)		write_f32_vector(vec->v.obj, vec->length, format)
#define print_vf64(vec)		write_f64_vector(vec->v.obj, vec->length, format)
#define print_vobj(vec)		write_obj_vector(vec->v.obj, vec->length, format)
#define print_code(value)	write_code(value, format)
#define print_parse(value)	write_parse(value, format)

/*
 * Prototypes
 */
static eint32_t
write_tag(etag_t *tag, eformat_t *format);

static eint32_t
write_code(east_list_t *list, eformat_t *format);

static eint32_t
write_parse(eobject_t cons, eformat_t *format);

static eint32_t
write_ast_node(east_node_t *node, eformat_t *format);

#define write_vector(id, type)						\
static eint32_t								\
write_##id##_vector(type *pointer, eint32_t length, eformat_t *format)
write_vector(i8, eint8_t);
write_vector(i16, eint16_t);
write_vector(u16, euint16_t);
write_vector(i32, eint32_t);
write_vector(u32, euint32_t);
write_vector(i64, eint_t);
write_vector(u64, euint_t);
write_vector(f32, efloat32_t);
write_vector(f64, efloat_t);
write_vector(obj, eobject_t);
#undef write_vector

static eint32_t
write_object(eobject_t object, eformat_t *format);

#define print_dfa_argument(value)	write_dfa_argument(value, format)
static eint32_t
write_dfa_argument(eobject_t value, eformat_t *format);

#define print_dfa(rule)			write_dfa(rule, format)
static eint32_t
write_dfa(edfa_rule_t *rule, eformat_t *format);

#define print_dfa_rules(symbol, hash)	write_dfa_rules(symbol, hash, format)
static eint32_t
write_dfa_rules(esymbol_t*, ehash_t*, eformat_t*);

#define print_dfa_symbols(vector)					\
    write_dfa_symbols(vector, function->vsym->v.obj, format)
static eint32_t
write_dfa_symbols(evector_t*, esymbol_t**, eformat_t*);

#define print_infer_type(type)		write_infer_type(type, format)
static eint32_t
write_infer_type(eint32_t type, eformat_t *format);

#define print_symbols(mask)						\
    write_symbols(function->vsym->v.obj, mask, format, width)
static eint32_t
write_symbols(esymbol_t **symbols, empz_t mask,
	      eformat_t *format, eint32_t width);

#if JITVM && DISASSEMBLER
static void
disasm_print_address(bfd_vma addr, struct disassemble_info *info);
#endif

/*
 * Initialization
 */
#define entry(name)		{ name, sizeof(name) - 1 }
static struct {
    char	*name;
    int		 size;
} codes[] = {
    entry("label"),		entry("nil"),
    entry("int"),		entry("long"),
    entry("float"),		entry("ll"),
    entry("ld"),		entry("ldt"),
    entry("lb"),		entry("lbt"),
    entry("ls"),		entry("lh"),
    entry("lhref"),		entry("lv"),
    entry("lvi"),		entry("lvref"),
    entry("lr"),		entry("lrref"),
    entry("lc"),		entry("lref"),
    entry("pd"),		entry("pdt"),
    entry("pb"),		entry("pbt"),
    entry("pv"),		entry("pvi"),
    entry("pr"),		entry("pp"),
    entry("anon"),		entry("sd"),
    entry("sdt"),		entry("sb"),
    entry("sbt"),		entry("ss"),
    entry("sst"),		entry("ssv"),
    entry("sh"),		entry("shref"),
    entry("sv"),		entry("svi"),
    entry("svref"),		entry("sr"),
    entry("srref"),		entry("sc"),
    entry("sref"),		entry("jt"),
    entry("jf"),		entry("j"),
    entry("jh"),		entry("jv"),
    entry("enter"),		entry("begin"),
    entry("off"),		entry("push"),
    entry("pusht"),		entry("pushv"),
    entry("pop"),		entry("call"),
    entry("meth"),		entry("ecm"),
    entry("blt"),		entry("ret"),
    entry("rett"),		entry("reti"),
    entry("retit"),		entry("ne"),
    entry("lt"),		entry("le"),
    entry("eq"),		entry("ge"),
    entry("gt"),		entry("and"),
    entry("or"),		entry("xor"),
    entry("mul2"),		entry("div2"),
    entry("shl"),		entry("shr"),
    entry("add"),		entry("sub"),
    entry("mul"),		entry("div"),
    entry("trunc2"),		entry("rem"),
    entry("atan2"),		entry("pow"),
    entry("hypot"),		entry("complex"),
    entry("inc"),		entry("dec"),
    entry("bool"),		entry("not"),
    entry("com"),		entry("inv"),
    entry("neg"),		entry("signbit"),
    entry("signum"),		entry("rational"),
    entry("integer_p"),		entry("rational_p"),
    entry("float_p"),		entry("real_p"),
    entry("complex_p"),		entry("number_p"),
    entry("finite_p"),		entry("inf_p"),
    entry("nan_p"),		entry("num"),
    entry("den"),		entry("real"),
    entry("imag"),		entry("arg"),
    entry("conj"),		entry("floor"),
    entry("trunc"),		entry("round"),
    entry("ceil"),		entry("abs"),
    entry("sqrt"),		entry("cbrt"),
    entry("sin"),		entry("cos"),
    entry("tan"),		entry("asin"),
    entry("acos"),		entry("atan"),
    entry("sinh"),		entry("cosh"),
    entry("tanh"), 		entry("asinh"),
    entry("acosh"), 		entry("atanh"),
    entry("proj"),		entry("exp"),
    entry("log"),		entry("log2"),
    entry("log10"),		entry("new"),
    entry("mv"),		entry("vnew"),
    entry("vnewi"),		entry("vmv"),
    entry("renew"),		entry("renewi"),
    entry("fref"),		entry("apply"),
    entry("aret"),		entry("szof"),
    entry("szofva"),		entry("szofdim"),
    entry("szofdimi"),		entry("tpof"),
    entry("subtpof"),		entry("rankof"),
    entry("vas"),		entry("val"),
    entry("lva"),		entry("lvai"),
    entry("sva"),		entry("svai"),
    entry("pva"),		entry("try"),
    entry("catch"),		entry("throw"),
    entry("unwind"),		entry("exit"),
    entry("setup"),		entry("intp_0"),
    entry("intp_v"),		entry("intp_n1"),
    entry("realp_v"),		entry("nump_0"),
    entry("nump_v"),		entry("#note"),
    entry("block"),
};

static struct {
    char	*name;
    int		 size;
} tokens[] = {
    entry("type"),		entry("symbol"),
    entry("number"),		entry("string"),
    entry("FILE"),		entry("LINE"),
    entry("FUNCTION"),		entry("VA_ARGS"),
    entry("DATE"),		entry("TIME"),
    entry("COUNTER"),		entry("stringify"),
    entry("concat"),		entry("list"),
    entry("elist"),		entry("vector"),
    entry("evector"),		entry("brace"),
    entry("ebrace"),		entry("progn"),
    entry("collon"),		entry("comma"),
    entry("field"),		entry("ellipsis"),
    entry("set"),		entry("andset"),
    entry("orset"),		entry("xorset"),
    entry("mul2set"),		entry("div2set"),
    entry("shlset"),		entry("shrset"),
    entry("addset"),		entry("subset"),
    entry("mulset"),		entry("divset"),
    entry("trunc2set"),		entry("remset"),
    entry("andand"),		entry("oror"),
    entry("ne"),		entry("lt"),
    entry("le"),		entry("eq"),
    entry("ge"),		entry("gt"),
    entry("and"),		entry("or"),
    entry("xor"),		entry("mul2"),
    entry("div2"),		entry("shl"),
    entry("shr"),		entry("add"),
    entry("sub"),		entry("mul"),
    entry("div"),		entry("trunc2"),
    entry("rem"),		entry("atan2"),
    entry("pow"),		entry("hypot"),
    entry("complex"),		entry("subtypeof"),
    entry("renew"),		entry("sizeof"),
    entry("typeof"),		entry("new"),
    entry("address"),		entry("pointer"),
    entry("rankof"),		entry("not"),
    entry("com"),		entry("plus"),
    entry("neg"),		entry("signbit"),
    entry("signum"),		entry("rational"),
    entry("integer_p"),		entry("rational_p"),
    entry("float_p"),		entry("real_p"),
    entry("complex_p"),		entry("number_p"),
    entry("finite_p"),		entry("inf_p"),
    entry("nan_p"),		entry("num"),
    entry("den"),		entry("real"),
    entry("imag"),		entry("arg"),
    entry("conj"),		entry("floor"),
    entry("trunc"),		entry("round"),
    entry("ceil"),		entry("abs"),
    entry("sqrt"),		entry("cbrt"),
    entry("sin"),		entry("cos"),
    entry("tan"),		entry("asin"),
    entry("acos"),		entry("atan"),
    entry("sinh"),		entry("cosh"),
    entry("tanh"), 		entry("asinh"),
    entry("acosh"), 		entry("atanh"),
    entry("proj"),		entry("exp"),
    entry("log"),		entry("log2"),
    entry("log10"),		entry("inc"),
    entry("dec"),		entry("postinc"),
    entry("postdec"),		entry("ternary"),
    entry("this"),		entry("if"),
    entry("else"),		entry("goto"),
    entry("return"),		entry("switch"),
    entry("case"),		entry("default"),
    entry("break"),		entry("continue"),
    entry("try"),		entry("catch"),
    entry("throw"),		entry("always"),
    entry("for"),		entry("do"),
    entry("while"),		entry("struct"),
    entry("class"),		entry("union"),
    entry("typedef"),		entry("label"),
    entry("code"),		entry("data"),
    entry("new_type_data"),	entry("function"),
    entry("declaration"),	entry("expression"),
    entry("statement"),		entry("offset"),
    entry("explicit"),		entry("ctor"),
};
#undef entry

#if JITVM && DISASSEMBLER
static bfd			 *disasm_bfd;
static disassemble_info		  disasm_info;
static disassembler_ftype	  disasm_print;
static asymbol			**disasm_symbols;
static asymbol			 *disasm_synthetic;
static long			  disasm_num_symbols;
static long			  disasm_num_synthetic;
#  define disasm_stream		stdout
#endif

/*
 * Implementation
 */
#if JITVM && DISASSEMBLER
/* Based on objdump source */
static int
disasm_compare_symbols(const void *ap, const void *bp)
{
    const asymbol	*a = *(const asymbol **)ap;
    const asymbol	*b = *(const asymbol **)bp;

    if (bfd_asymbol_value(a) > bfd_asymbol_value(b))
	return (1);
    if (bfd_asymbol_value(a) < bfd_asymbol_value(b))
	return (-1);
    return (0);
}
#endif

void
init_debug(void)
{
#if JITVM && DISASSEMBLER
    bfd_init();
    disasm_bfd = bfd_openr(cfg_progname, null);
    assert(disasm_bfd != null);
    bfd_check_format(disasm_bfd, bfd_object);
    bfd_check_format(disasm_bfd, bfd_archive);
    disasm_print = disassembler(disasm_bfd);
    assert(disasm_print != null);
    INIT_DISASSEMBLE_INFO(disasm_info, disasm_stream, fprintf);
#  if defined(__i386__) || defined(__x86_64__)
    disasm_info.arch = bfd_arch_i386;
#    if defined(__x86_64__)
    disasm_info.mach = bfd_mach_x86_64;
#    else
    disasm_info.mach = bfd_mach_i386_i386;
#    endif
#  endif
#  if defined(__arm__)
    /* FIXME add mapping for prolog switching to arm and possible jump
     * before first prolog also in arm mode */
    if (jit_cpu.thumb)
	disasm_info.disassembler_options = "force-thumb";
#  endif
    disasm_info.print_address_func = disasm_print_address;

    eadd_root((eobject_t *)&disasm_symbols);

    if (bfd_get_file_flags(disasm_bfd) & HAS_SYMS) {
	asymbol		**in;
	asymbol		**out;
	asymbol		 *symbol;
	long		  offset;
	long		  sym_count;
	long		  dyn_count;
	long		  sym_storage;
	long		  dyn_storage;

	sym_storage = bfd_get_symtab_upper_bound(disasm_bfd);
	assert(sym_storage >= 0);

	if (bfd_get_file_flags(disasm_bfd) & DYNAMIC) {
	    dyn_storage = bfd_get_dynamic_symtab_upper_bound(disasm_bfd);
	    assert(dyn_storage >= 0);
	}
	else
	    dyn_storage = 0;

	enew_object((eobject_t *)&disasm_symbols, t_resize | t_void,
		    sym_storage + dyn_storage);
	sym_count = bfd_canonicalize_symtab(disasm_bfd, disasm_symbols);
	assert(sym_count >= 0);
	if (dyn_storage) {
	    dyn_count = bfd_canonicalize_dynamic_symtab(disasm_bfd,
							disasm_symbols +
							sym_count);
	    assert(dyn_count >= 0);
	}
	else
	    dyn_count = 0;
	disasm_num_symbols = sym_count + dyn_count;

	disasm_num_synthetic = bfd_get_synthetic_symtab(disasm_bfd,
							sym_count,
							disasm_symbols,
							dyn_count,
							disasm_symbols +
							sym_count,
							&disasm_synthetic);
	if (disasm_num_synthetic > 0) {
	    eresize_object((eobject_t *)&disasm_symbols,
			   sym_storage + dyn_storage +
			   disasm_num_synthetic * sizeof(asymbol *));
	    for (offset = 0; offset < disasm_num_synthetic; offset++)
		disasm_symbols[disasm_num_symbols++] =
		    disasm_synthetic + offset;
	}

	/* remove symbols not useful for disassemble */
	in = out = disasm_symbols;
	for (offset = 0; offset < disasm_num_symbols; offset++) {
	    symbol = *in++;
	    if (symbol->name != null &&
		symbol->name[0] != '\0' &&
		!(symbol->flags & (BSF_DEBUGGING | BSF_SECTION_SYM)) &&
		!bfd_is_und_section(symbol->section) &&
		!bfd_is_com_section(symbol->section))
		*out++ = symbol;
	}
	disasm_num_symbols = out - disasm_symbols;
	qsort(disasm_symbols, disasm_num_symbols,
	      sizeof(asymbol *), disasm_compare_symbols);
    }
#endif
}

void
finish_debug(void)
{
#if JITVM && DISASSEMBLER
    if (disasm_synthetic)
	free(disasm_synthetic);
    erem_root((eobject_t *)&disasm_symbols);
#endif
}

void
ewrite_object(eobject_t object)
{
    eformat_t	 local;
    eformat_t	*format;

    format = &local;
    memset(format, 0, sizeof(eformat_t));
    local.read = 1;
    local.radix = 10;
    print_obj(object);
    dputc('\n');
    eflush(std_output);
}

void
ewrite_parse(eobject_t list)
{
    eformat_t	 local;
    eformat_t	*format;

    format = &local;
    memset(format, 0, sizeof(eformat_t));
    local.read = 1;
    local.radix = 10;
    if (econs_p(list))
	print_parse(list);
    else
	print_obj(list);
    dputc('\n');
    eflush(std_output);
}

void
ewrite_code(east_list_t *list)
{
    eformat_t	 local;
    eformat_t	*format;

    format = &local;
    memset(format, 0, sizeof(eformat_t));
    local.read = 1;
    local.radix = 10;
    if (etype(list) == t_ast_list)
	print_code(list);
    else
	print_obj(list);
    dputc('\n');
    eflush(std_output);
}

static eint32_t
write_tag(etag_t *tag, eformat_t *format)
{
    eint32_t		bytes;
    switch (etype(tag->data)) {
	case t_basic:		case t_struct:		case t_class:
	case t_union:
	    bytes = print_sym(((erecord_t *)tag->data)->name);
	    break;
	default:
	    bytes = dputs("auto", 4);
	    break;
    }
    return (bytes);
}

static eint32_t
write_code(east_list_t *list, eformat_t *format)
{
    eint32_t		bytes;
    dputc('<');		bytes = 1;
    bytes += print_ast(east_node(list));
    dputc('>');		++bytes;
    for (list = east_next(list); list; list = east_next(list)) {
	bytes += dputs(" <", 2);
	bytes += print_ast(east_node(list));
	dputc('>');	++bytes;
    }
    return (bytes);
}

static eint32_t
write_parse(eobject_t cons, eformat_t *format)
{
    eint32_t	bytes;
    eobject_t	first;
    eint32_t	offset;
    dputc('(');		bytes = 1;
    first = ecar(cons);
    if (efixnum_p(first)) {
	offset = eget_fixnum(first);
	bytes += dputs(tokens[offset].name, tokens[offset].size);
    }
    else if (econs_p(first))
	bytes += print_parse(first);
    else
	bytes += print_obj(first);
    for (cons = ecdr(cons); econs_p(cons); cons = ecdr(cons)) {
	dputc(' ');	++bytes;
	first = ecar(cons);
	if (econs_p(first))
	    bytes += print_parse(first);
	else
	    bytes += print_obj(first);
    }
    if (cons) {
	bytes += dputs(" . ", 3);
	bytes += print_obj(cons);
    }
    dputc(')');		++bytes;
    return (bytes);
}

static eint32_t
write_ast_node(east_node_t *node, eformat_t *format)
{
    eint32_t	bytes;
    eint32_t	offset;
    eint32_t	length;
    bytes = dputs(codes[node->code].name, codes[node->code].size);
    switch (node->code) {
	case eop_label:			case eop_block:
	    bytes += dputs(" L", 2);
	    bytes += print_int(node->clabel.label->ident);
	    bytes += dputs(": ", 2);
	    bytes += print_int(node->clabel.label->loop);
	    if (node->clabel.label->ijmp)
		bytes += dputs(" ijmp", 5);
	    if (node->clabel.label->ojmp)
		bytes += dputs(" ojmp", 5);
	    if (node->clabel.label->sref)
		bytes += dputs(" sref", 5);
	    break;
	case eop_nil:
	    break;
	case eop_int:			case eop_long:
	    dputc(' ');		++bytes;
	    bytes += print_int(immediate_int(node));
	    break;
	case eop_float:
	    dputc(' ');		++bytes;
	    bytes += print_flt(immediate_float(node));
	    break;
	case eop_ll:
	    dputc(' ');		++bytes;
	    bytes += print_obj(els[immediate_offset(node)]);
	    break;
	case eop_ld:			case eop_sd:
	case eop_pd:			case eop_lb:
	case eop_sb:			case eop_pb:
	    dputc(' ');		++bytes;
	    bytes += print_sym(node->csymbol.symbol);
	    dputc('#');		++bytes;
	    bytes += print_int(node->csymbol.ssa);
	    break;
	case eop_ldt:			case eop_sdt:
	case eop_pdt:			case eop_lbt:
	case eop_sbt:			case eop_pbt:
	case eop_lc:			case eop_sc:
	    dputc(' ');		++bytes;
	    bytes += print_sym(node->csymbol.symbol);
	    dputc('#');		++bytes;
	    bytes += print_int(node->csymbol.ssa);
	    dputc(' ');		++bytes;
	    if (node->csymbol.symbol->tag)
		bytes += print_tag(node->csymbol.symbol->tag);
	    else
		bytes += dputs("auto", 4);
	    break;
	case eop_setup:
	    dputc(' ');		++bytes;
	    bytes += print_sym(node->csymbol.symbol);
	    dputc(' ');		++bytes;
	    if (node->csymbol.symbol->tag)
		bytes += print_tag(node->csymbol.symbol->tag);
	    else
		bytes += dputs("auto", 4);
	    break;
	case eop_lh:			case eop_sh:
	    break;
	case eop_lhref:			case eop_shref:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->chash.arg);
	    break;
	case eop_lv:			case eop_sv:
	case eop_pv:			case eop_off:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cvector.rank);
	    break;
	case eop_lvi:			case eop_svi:
	case eop_pvi:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cvector.offset);
	    break;
	case eop_lvref:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cvector.rank);
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cvector.arg);
	    break;
	case eop_svref:			case eop_szofdimi:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cvector.arg);
	    break;
	case eop_vnew:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cvector.type);
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cvector.rank);
	    break;
	case eop_renew:			case eop_szof:
	case eop_szofdim:
	    break;
	case eop_renewi:		case eop_vmv:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cvector.length);
	    break;
	case eop_vnewi:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cvector.type);
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cvector.length);
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cvector.rank);
	    for (offset = 0; offset < node->cvector.rank; offset++) {
		dputc(' ');	++bytes;
		bytes += print_int(node->cvector.dims[offset]);
	    }
	    break;
	case eop_lr:			case eop_sr:
	case eop_pr:
	case eop_lrref:
	    dputc(' ');		++bytes;
	    bytes += print_rec(node->crecord.record);
	    dputc('.');		++bytes;
	    bytes += print_sym(node->crecord.symbol);
	    dputc(' ');		++bytes;
	    if (node->crecord.symbol->tag)
		bytes += print_tag(node->crecord.symbol->tag);
	    else
		bytes += dputs("auto", 4);
	    if (node->code == eop_lrref) {
		dputc(' ');	++bytes;
		bytes += print_int(node->crecord.arg);
	    }
	    break;
	case eop_srref:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->crecord.arg);
	    break;
	case eop_tpof:			case eop_subtpof:
	    break;
	case eop_new:			case eop_anon:
	    dputc(' ');		++bytes;
	    bytes += print_rec(node->crecord.record);
	    break;
	case eop_mv:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->crecord.record->size);
	    break;
	case eop_ls:			case eop_ss:
	case eop_ssv:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cstack.offset);
	    break;
	case eop_sst:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cstack.offset);
	    dputc(' ');		++bytes;
	    bytes += print_tag(node->cstack.tag);
	    break;
	case eop_push:			case eop_pushv:
	case eop_pop:
	    break;
	case eop_pusht:
	    dputc(' ');		++bytes;
	    bytes += print_tag(node->cstack.tag);
	    break;
	case eop_j:			case eop_jt:
	case eop_jf:
	    bytes += dputs(" L", 2);
	    /* help to allow calling ewrite_object from gdb */
	    if (node->clabel.label)
		bytes += print_int(node->clabel.label->ident);
	    else {
		dputc('?');
		++bytes;
	    }
	    break;
	case eop_jh:			case eop_jv:
	    dputc(' ');		++bytes;
	    bytes += print_int(immediate_offset(node));
	    break;
	case eop_enter:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->center.local);
	    dputc(' ');		++bytes;
	    bytes += print_int(node->center.extra);
	    dputc(' ');		++bytes;
	    bytes += print_int(node->center.acount);
	    dputc(' ');		++bytes;
	    bytes += print_int(node->center.lcount);
	    length = node->center.acount + node->center.lcount;
	    for (offset = 0; offset < length; offset++) {
		dputc(' ');	++bytes;
		bytes += print_int(node->center.types[offset]);
	    }
	    break;
	case eop_begin:			case eop_lva:
	case eop_pva:			case eop_sva:
	case eop_val:			case eop_vas:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cstack.length);
	    break;
	case eop_lvai:			case eop_svai:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cstack.length);
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cstack.offset);
	    break;
	case eop_call:
	    dputc(' ');		++bytes;
	    bytes += print_sym(node->capply.symbol);
	    break;
	case eop_blt:
	    dputc(' ');		++bytes;
	    bytes += print_sym(node->capply.symbol);
	    dputc(' ');		++bytes;
	    bytes += print_int(node->capply.length);
	    break;
	case eop_ecm:
	    dputc(' ');		++bytes;
	    if (node->capply.symbol->a_ctor)
		bytes += print_sym(((efunction_t *)node->capply.symbol->value)
			  ->record->name);
	    else
		bytes += print_sym(node->capply.symbol);
	    dputc(' ');		++bytes;
	    bytes += print_rec(record_vector[node->capply.type]);
	    break;
	case eop_meth:
	    dputc(' ');		++bytes;
	    bytes += print_sym(node->capply.symbol);
	    dputc(' ');		++bytes;
	    bytes += print_rec(record_vector[node->capply.type]);
	    break;
	case eop_apply:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->capply.length);
	    break;
	case eop_fref:
	    dputc(' ');		++bytes;
	    bytes += print_sym(node->capply.symbol);
	    dputc(' ');		++bytes;
	    bytes += print_int(node->capply.length);
	    dputc(' ');		++bytes;
	    bytes += print_int(node->capply.arg);
	    break;
	case eop_ret:
	    break;
	case eop_rett:
	    dputc(' ');		++bytes;
	    bytes += print_tag(node->creturn.tag);
	    break;
	case eop_reti:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->creturn.length);
	    break;
	case eop_retit:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->creturn.length);
	    dputc(' ');		++bytes;
	    bytes += print_tag(node->creturn.tag);
	    break;
	case eop_ne:			case eop_lt:
	case eop_le:			case eop_eq:
	case eop_ge:			case eop_gt:
	case eop_and:			case eop_or:
	case eop_xor:			case eop_mul2:
	case eop_div2:			case eop_shl:
	case eop_shr:			case eop_add:
	case eop_sub:			case eop_mul:
	case eop_div:			case eop_trunc2:
	case eop_rem:			case eop_atan2:
	case eop_pow:			case eop_hypot:
	case eop_complex:		case eop_inc:
	case eop_dec:			case eop_bool:
	case eop_not:			case eop_com:
	case eop_inv:			case eop_neg:
	case eop_signbit:		case eop_signum:
	case eop_rational:		case eop_integer_p:
	case eop_rational_p:		case eop_float_p:
	case eop_real_p:		case eop_complex_p:
	case eop_number_p:		case eop_finite_p:
	case eop_inf_p:			case eop_nan_p:
	case eop_num:			case eop_den:
	case eop_real:			case eop_imag:
	case eop_arg:			case eop_conj:
	case eop_floor:			case eop_trunc:
	case eop_round:			case eop_ceil:
	case eop_abs:			case eop_sqrt:
	case eop_cbrt:			case eop_sin:
	case eop_cos:			case eop_tan:
	case eop_asin:			case eop_acos:
	case eop_atan:			case eop_sinh:
	case eop_cosh:			case eop_tanh:
	case eop_asinh:			case eop_acosh:
	case eop_atanh:			case eop_proj:
	case eop_exp:			case eop_log:
	case eop_log2:			case eop_log10:
	case eop_lref:			case eop_sref:
	case eop_pp:			case eop_szofva:
	case eop_aret:			case eop_rankof:
	case eop_try:			case eop_unwind:
	case eop_catch:
	    break;
	case eop_throw:
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cexcept.arg);
	    break;
	case eop_exit:			case eop_intp_0:
	case eop_intp_v:		case eop_intp_n1:
	case eop_realp_v:		case eop_nump_0:
	case eop_nump_v:
	    break;
	case eop_note:
	    dputc(' ');		++bytes;
	    bytes += print_str(node->cnote.name);
	    dputc(' ');		++bytes;
	    bytes += print_int(node->cnote.line);
	    break;
    }

    return (bytes);
}

#define write_int_vector(id, type)					\
static eint32_t								\
write_##id##_vector(type *pointer, eint32_t length, eformat_t *format)	\
{									\
    eint32_t		bytes;						\
    for (bytes = 0; length > 1; length--, pointer++, bytes++) {		\
	bytes += print_int(*pointer);					\
	dputc(' ');							\
    }									\
    bytes += print_int(*pointer);					\
    return (bytes);							\
}
write_int_vector(i8, eint8_t)
write_int_vector(i16, eint16_t)
write_int_vector(u16, euint16_t)
write_int_vector(i32, eint32_t)
write_int_vector(u32, euint32_t)
write_int_vector(i64, eint_t)
#undef write_int_vector

static eint32_t
write_u64_vector(euint_t *pointer, eint32_t length, eformat_t *format)
{
    eint32_t	bytes;
    eint_t	integer;
    for (bytes = 0; length > 1; length--, pointer++, bytes++) {
	integer = *pointer;
	if (likely(integer >= 0))
	    bytes += print_int(integer);
	else {
	    empz_set_u(thr_zr, integer);
	    bytes += print_mpz(thr_zr);
	}
	dputc(' ');
    }
    integer = *pointer;
    if (likely(integer >= 0))
	bytes += print_int(integer);
    else {
	empz_set_u(thr_zr, integer);
	bytes += print_mpz(thr_zr);
    }
    return (bytes);
}

#define write_flt_vector(id, type)					\
static eint32_t								\
write_##id##_vector(type *pointer, eint32_t length, eformat_t *format)	\
{									\
    eint32_t		bytes;						\
    for (bytes = 0; length > 1; length--, pointer++, bytes++) {		\
	bytes += print_flt(*pointer);					\
	dputc(' ');							\
    }									\
    bytes += print_flt(*pointer);					\
    return (bytes);							\
}
write_flt_vector(f32, efloat32_t)
write_flt_vector(f64, efloat_t)
#undef write_flt_vector

static eint32_t
write_obj_vector(eobject_t *pointer, eint32_t length, eformat_t *format)
{
    eint32_t		bytes;
    for (bytes = 0; length > 1; length--, pointer++, bytes++) {
	bytes += print_obj(*pointer);
	dputc(' ');
    }
    bytes += print_obj(*pointer);
    return (bytes);
}

static eint32_t
write_object(eobject_t object, eformat_t *format)
{
    etype_t		 type;
    eint32_t		 bytes;
    eentry_t		*entry;
    ebool_t		 first;
    eint32_t		 offset;
    union {
	ecdd_t		*cdd;
	ecqq_t		 cqq;
	eentry_t	*entry;
	ehash_t		*hash;
	eint_t		*int_pointer;
	efloat_t	*float_pointer;
	elabel_t	*label;
	east_list_t	*list;
	empz_t		 mpz;
	empq_t		 mpq;
	empr_t		 mpr;
	empc_t		 mpc;
	east_node_t	*node;
	eobject_t	 object;
	erecord_t	*record;
	edfa_rule_t	*rule;
	esymbol_t	*symbol;
	etag_t		*tag;
	evector_t	*vector;
    } data;
    data.object = object;
    switch ((type = etype(object) & ~t_const)) {
	case t_fixnum:
	    bytes = print_int(eget_fixnum(object));
	    break;
	case t_int:
	    bytes = print_int(*data.int_pointer);
	    break;
	case t_float:
	    bytes = print_flt(*data.float_pointer);
	    break;
	case t_mpz:
	    bytes = print_mpz(data.mpz);
	    break;
	case t_mpq:
	    bytes = print_mpq(data.mpq);
	    break;
	case t_mpr:
	    bytes = print_mpr(data.mpr);
	    break;
	case t_cdd:
	    bytes = print_cdd(data.cdd);
	    break;
	case t_cqq:
	    bytes = print_cqq(data.cqq);
	    break;
	case t_mpc:
	    bytes = print_mpc(data.mpc);
	    break;
	case t_symbol:
	    bytes = print_sym(data.symbol);
	    break;
	case t_void:
	    bytes = dputs("nil", 3);
	    break;
	case t_cons:
	    dputc('(');		bytes = 1;
	    bytes += print_obj(ecar(data.object));
	    data.object = ecdr(data.object);
	    for (; econs_p(data.object); data.object = ecdr(data.object)) {
		dputc(' ');	++bytes;
		bytes += print_obj(ecar(data.object));
	    }
	    if (data.object) {
		bytes += dputs(" . ", 3);
		bytes += print_obj(data.object);
	    }
	    dputc(')');		++bytes;
	    break;
	case t_basic:		case t_struct:		case t_class:
	case t_union:
	    data.symbol = data.record->name;
	    bytes = print_sym(data.symbol);
	    break;
	case t_tag:
	    bytes = print_tag(data.tag);
	    break;
	case t_hash:
	    first = true;
	    bytes = dputs("#h(", 3);
	    for (offset = 0; offset < data.hash->size; offset++) {
		entry = data.hash->entries[offset];
		for (; entry; entry = entry->next) {
		    if (first)
			first = false;
		    else {
			dputc(' ');
			++bytes;
		    }
		    bytes += print_obj(entry);
		}
	    }
	    dputc(')');		++bytes;
	    break;
	case t_entry:
	    bytes = dputs("#e(", 3);
	    bytes += print_obj(data.entry->name);
	    dputc(' ');		++bytes;
	    bytes += print_obj(data.entry->value);
	    dputc(')');		++bytes;
	    break;
	case t_string:
	    bytes = print_str(data.vector);
	    break;
	case t_code_label:	case t_code_simple:	case t_code_immediate:
	case t_code_stack:	case t_code_symbol:	case t_code_hash:
	case t_code_vector:	case t_code_record:	case t_code_class:
	case t_code_enter:	case t_code_apply:	case t_code_return:
	case t_code_logic:	case t_code_except:	case t_code_note:
	    bytes = dputs("#n(", 3);
	    bytes += print_ast(data.node);
	    dputc(')');		++bytes;
	    break;
	case t_ast_list:
	    bytes = print_code(data.list);
	    break;
	case t_label:
	    bytes = dputs("#l(", 3);
	    bytes += print_int(data.label->ident);
	    dputc(' ');		++bytes;
	    bytes += print_int(data.label->loop);
	    if (data.label->sref)
		bytes += dputs(" sref", 5);
	    dputc(')');		++bytes;
	    break;
	case t_dfa_rule:
	    bytes = dputs("#d(", 4);
	    bytes += print_obj(data.rule->value);
	    bytes += print_dfa(data.rule);
	    dputc(')');		++bytes;
	    break;
	default:
	    bytes = 0;
	    if (evector_type_p(type)) {
		type &= ~t_vector;
		dputc('#');	++bytes;
		if (type < ets_idx)
		    bytes += print_obj(record_vector[type]);
		else
		    bytes += dputs("auto", 4);
		dputc('(');	++bytes;
		switch (type) {
		    case t_int8:
			bytes += print_vi8(data.vector);
			break;
		    case t_int16:
			bytes += print_vi16(data.vector);
			break;
		    case t_uint16:
			bytes += print_vu16(data.vector);
			break;
		    case t_int32:
			bytes += print_vi32(data.vector);
			break;
		    case t_uint32:
			bytes += print_vu32(data.vector);
			break;
		    case t_int:
			bytes += print_vi64(data.vector);
			break;
		    case t_uint:
			bytes += print_vu64(data.vector);
			break;
		    case t_float32:
			bytes += print_vf32(data.vector);
			break;
		    case t_float:
			bytes += print_vf64(data.vector);
			break;
		    default:
			bytes += print_vobj(data.vector);
			break;
		}
		dputc(')');	++bytes;
	    }
	    else if (type < ets_idx) {
		bytes += dputs("#r(", 3);
		bytes += print_obj(record_vector[type]);
		dputc(' ');	++bytes;
		format->radix = 16;
		bytes += print_ptr(data.object);
		format->radix = 10;
		dputc(')');	++bytes;
	    }
	    else {
		format->radix = 16;
		bytes += print_ptr(data.object);
		format->radix = 10;
	    }
	    break;
    }
    return (bytes);
}

static int
write_symbols(esymbol_t **symbols, empz_t mask,
	      eformat_t *format, eint32_t width)
{
    unsigned long	 bit;
    esymbol_t		*atom;
    eint32_t		 bytes;
    eint32_t		 offset;
    static eint8_t	 spaces[32] = "                                ";

    bytes = 0;
    for (bit = mpz_scan1(mask, 0);
	 bit != ULONG_MAX; bit = mpz_scan1(mask, bit)) {
	atom = symbols[bit];
	offset = width - bit + 5;
	dputs("\n;", 2);
	bytes = offset + 1;
	while (offset > 32) {
	    dputs(spaces, 32);
	    offset -= 32;
	}
	if (offset)
	    dputs(spaces, offset);
	bytes += print_sym(atom);
	++bit;
    }

    return (bytes);
}

static eint32_t
write_dfa_argument(eobject_t value, eformat_t *format)
{
    east_node_t		*node;
    eint32_t		 bytes;
    esymbol_t		*symbol;

    if (esymbol_p(value)) {
	symbol = value;
	return (print_sym(symbol));
    }
    node = value;
    switch (node->code) {
	case eop_nil:
	    bytes = dputs("null", 4);
	    break;
	case eop_int:	case eop_long:
	    bytes = print_int(immediate_int(node));
	    break;
	case eop_float:
	    bytes = print_flt(immediate_float(node));
	    break;
	default:
	    bytes = print_obj(els[immediate_offset(node)]);
	    break;
    }

    return (bytes);
}

static eint32_t
write_dfa(edfa_rule_t *rule, eformat_t *format)
{
    eint32_t		 bytes;

    dputc(' ');				bytes = 1;
    if (rule->data.bits.lt) {
	if (rule->data.bits.gt)		bytes += dputs("!= ", 3);
	else if (rule->data.bits.eq)	bytes += dputs("<= ", 3);
	else				bytes += dputs("< ", 2);
    }
    else if (rule->data.bits.gt) {
	if (rule->data.bits.eq)		bytes += dputs(">= ", 3);
	else				bytes += dputs("> ", 2);
    }
    else				bytes += dputs("== ", 3);
    bytes += print_dfa_argument(rule->name);

    return (bytes);
}

static eint32_t
write_dfa_rules(esymbol_t *symbol, ehash_t *hash, eformat_t *format)
{
    edfa_rule_t		*rule;
    eint32_t		 bytes;
    eint32_t		 offset;

    bytes = 0;
    /* check if is a hash to not crash if called from gdb during dfa */
    if (ehash_p(hash)) {
	for (offset = 0; offset < hash->size; offset++) {
	    rule = (edfa_rule_t *)hash->entries[offset];
	    for (; rule; rule = rule->next) {
		bytes = 4;
		dputs("\n;-> ", 5);
		bytes += print_sym(symbol);
		bytes += print_dfa(rule);
	    }
	}
    }

    return (bytes);
}

static eint32_t
write_dfa_symbols(evector_t *vector, esymbol_t **symbols, eformat_t *format)
{
    eint32_t		  bytes;
    ehash_t		**hashes;
    eint32_t		  offset;

    bytes = 0;
    if (vector) {
	hashes = vector->v.obj;
	for (offset = 0; offset < vector->length; offset++) {
	    if (hashes[offset])
		bytes += print_dfa_rules(symbols[offset], hashes[offset]);
	}
    }

    return (bytes);
}

static eint32_t
write_infer_type(eint32_t type, eformat_t *format)
{
    eint32_t		 length;
    char		*string;

    switch (type) {
	case t_void:		string = "void";		break;
	case t_int8:		string = "int8";		break;
	case t_uint8:		string = "uint8";		break;
	case t_int16:		string = "int16";		break;
	case t_uint16:		string = "uint16";		break;
	case t_int32:		string = "int32";		break;
	case t_uint32:		string = "uint32";		break;
	case t_int:		string = "int64";		break;
	case t_uint:		string = "uint64";		break;
	case t_float32:		string = "float32";		break;
	case t_float:		string = "float64";		break;
	case t_mpz:		string = "mpz";			break;
	case t_mpq:		string = "mpq";			break;
	case t_mpr:		string = "mpr";			break;
	case t_cdd:		string = "cdd";			break;
	case t_cqq:		string = "cqq";			break;
	case t_mpc:		string = "mpc";			break;
	case t_exact:		string = "exact";		break;
	case t_inexact:		string = "inexact";		break;
	case t_lambda:		string = "lambda";		break;
	case t_vector:		string = "vector";		break;
	case t_pointer:		string = "pointer";		break;
	case t_dynamic:		string = "dynamic";		break;
	default:
	    if (type & t_pointer)
		string = "pointer";
	    else if (type & t_vector)
		string = "vector";
	    else
		string = "record";
	    break;
    }
    length = strlen(string);
    dputs(string, length);

    return (length);
}

void
ewrite_function(efunction_t *function)
{
    east_node_t		 *node;
    east_list_t		 *list;
    ehash_t		 *hash;
    elabel_t		 *temp;
    eint32_t		  bytes;
    elabel_t		 *label;
    eentry_t		 *entry;
    eint32_t		  width;
    eformat_t		  local;
    eformat_t		 *format;
    elabel_t		**labels;
    eint32_t		  offset;
    char		 *spaces = "                                        ";

    label = null;
    format = &local;
    memset(&local, 0, sizeof(eformat_t));
    local.read = 1;
    local.radix = 10;
    width = function->vsym->offset;

    if (function->record) {
	print_sym(function->record->name);
	dputc('.');
	if (function->name->a_ctor)
	    print_sym(function->record->name);
	else
	    print_sym(function->name);
    }
    else
	print_obj(function->name);
    dputs("()\n", 3);
    for (list = function->code; list; list = east_next(list)) {
	node = east_node(list);
	switch (node->code) {
	    case eop_block:			case eop_label:
		bytes = 1;
		label = node->clabel.label;
		dputc('L');
		bytes += print_int(label->ident);
		bytes += dputs(": ", 2);
		bytes += print_int(label->loop);
		if (label->sref)
		    bytes += dputs(" sref", 5);
		if (label->dfa) {
		    if ((offset = print_dfa_symbols(label->dfa)))
			bytes = offset;
		}
		format->read = 0;
		format->radix = 2;
		format->width = width;
		if (mpz_sgn(label->use)) {
		    dputs("\n; use: ", 8);
		    print_mpz(label->use);
		    bytes = print_symbols(label->use);
		}
		if (mpz_sgn(label->set)) {
		    dputs("\n; set: ", 8);
		    print_mpz(label->set);
		    bytes = print_symbols(label->set);
		}
		if (mpz_sgn(label->phi)) {
		    dputs("\n; phi: ", 8);
		    print_mpz(label->phi);
		    bytes = print_symbols(label->phi);
		}
		format->width = 0;
		format->radix = 10;
		format->read = 1;
		if (label->stack) {
		    bytes = 9;
		    dputs("\n; stack: ", 10);
		    bytes += print_int(label->stack);
		}
		if (label->srcs->offset) {
		    bytes = 8;
		    dputs("\n;  srcs:", 9);
		    labels = label->srcs->v.obj;
		    for (offset = 0; offset < label->srcs->offset; offset++) {
			temp = labels[offset];
			bytes += dputs(" L", 2);
			bytes += print_int(temp->ident);
		    }
		}
		if (label->dsts->offset) {
		    bytes = 8;
		    dputs("\n;  dsts:", 9);
		    labels = label->dsts->v.obj;
		    for (offset = 0; offset < label->dsts->offset; offset++) {
			temp = labels[offset];
			bytes += dputs(" L", 2);
			bytes += print_int(temp->ident);
		    }
		}
		break;
	    case eop_j:
		bytes = dputs("  j L", 5);
		bytes += print_int(node->clabel.label->ident);
		break;
	    case eop_jf:
		bytes = dputs(" jf L", 5);
		bytes += print_int(node->clabel.label->ident);
		break;
	    case eop_jt:
		bytes = dputs(" jt L", 5);
		bytes += print_int(node->clabel.label->ident);
		break;
	    case eop_jh:			case eop_jv:
		bytes = dputs(" jh ", 4);
		hash = els[immediate_offset(node)];
#if JITVM
		if (etype(hash) != t_hash && eswitch_hash) {
		    entry = eget_hash(eswitch_hash,
				      efixnum(immediate_offset(node)));
		    hash = entry->value;
		}
#endif
		if (hash) {
		    for (offset = 0; offset < hash->size; offset++) {
			entry = hash->entries[offset];
			for (; entry; entry = entry->next) {
			    bytes += print_obj(entry->name);
			    bytes += dputs(":L", 2);
			    node = entry->value;
			    bytes += print_int(node->clabel.label->ident);
			    dputc(' ');	++bytes;
			}
		    }
		}
		break;
	    default:
		bytes = dputs("    ", 4);
		bytes += print_ast(node);
		break;
	}

	if (bytes < 40)
	    dputs(spaces, 40 - bytes);
	dputs("; ", 2);

	switch (node->code) {
	    case eop_ne:	case eop_lt:		case eop_le:
	    case eop_eq:	case eop_ge:		case eop_gt:
	    case eop_and:	case eop_or:		case eop_xor:
	    case eop_mul2:	case eop_div2:		case eop_shl:
	    case eop_shr:	case eop_add:		case eop_sub:
	    case eop_mul:	case eop_div:		case eop_trunc2:
	    case eop_rem:	case eop_atan2:		case eop_pow:
	    case eop_hypot:	case eop_complex:
		print_infer_type(list->ltype);
		dputc('/');
	    default:
		print_infer_type(list->rtype);
		dputc(':');
		/* shared nodes with misleading debug information */
	    case eop_nil:	case eop_int:		case eop_long:
	    case eop_ll:	case eop_ld:		case eop_lb:
	    case eop_ldt:	case eop_lbt:
	    case eop_lc:
		break;
	}

	print_infer_type(list->itype);
	dputc('\n');
    }
    dputs("---------------\n", 16);
    eflush(std_output);
}

#if JITVM
#  if DISASSEMBLER
/* Based on version in main.c of GNU Smalltalk */

#    if __WORDSIZE == 32
#      define address_buffer_length		16
#      define address_buffer_format		"%llx"
#else
#      define address_buffer_length		32
#      define address_buffer_format		"%lx"
#endif
static void
disasm_print_address(bfd_vma addr, struct disassemble_info *info)
{
    evector_t		*name;
    eint32_t		 line;
    char		 buffer[address_buffer_length];

    sprintf(buffer, address_buffer_format, (eint_t)addr);
    (*info->fprintf_func)(info->stream, "0x%s", buffer);

    if (ejit_pointer_p((eobject_t)(eword_t)addr)) {
	if (eget_note((eobject_t)(eword_t)addr, null, &name, &line) && name)
	    (*info->fprintf_func)(info->stream, " %*s:%d",
				  name->length, name->v.u8,
				  line);
	else
	    (*info->fprintf_func)(info->stream, " null:0");
    }
    else if (disasm_num_symbols) {
	long		 low;
	long		 high;
	long		 offset;
	asymbol		*symbol;

	low = 0;
	high = disasm_num_symbols;
	do {
	    offset = (low + high) >> 1;
	    symbol = disasm_symbols[offset];
	    if (bfd_asymbol_value(symbol) > addr)
		high = offset - 1;
	    else if (bfd_asymbol_value(symbol) < addr)
		low = offset + 1;
	    else
		break;
	} while (low < high);

	if (offset >= 0 && offset < disasm_num_symbols) {
	    if (bfd_asymbol_value(symbol) < addr) {
		while (++offset < disasm_num_symbols) {
		    symbol = disasm_symbols[offset];
		    if (bfd_asymbol_value(symbol) >= addr)
			break;
		}
	    }
	    else if (bfd_asymbol_value(symbol) > addr) {
		while (offset--) {
		    if (bfd_asymbol_value(disasm_symbols[offset]) < addr)
			break;
		    symbol = disasm_symbols[offset];
		}
	    }
	    if (bfd_asymbol_value(symbol) == addr)
		(*info->fprintf_func)(info->stream, " # %s", symbol->name);
	}
    }
}

void
edisassemble(eobject_t code, eint32_t length)
{
    evector_t		*name;
    eint32_t		 line;
    int			 bytes;
    evector_t		*old_name;
    eint32_t		 old_line;
#    if __arm__
    eint32_t		*data_info;
    eint32_t		 data_offset;
#    endif
    bfd_vma		 pc = (euword_t)code;
    bfd_vma		 end = (euword_t)code + length;
    char		 buffer[address_buffer_length];

#    if __arm__
    if (_jit->data_info)
	data_info = _jit->data_info->ptr;
    else
	data_info = null;
    data_offset = 0;
#    endif
    disasm_info.buffer = code;
    disasm_info.buffer_vma = (euword_t)code;
    disasm_info.buffer_length = length;
    old_name = null;
    old_line = 0;
    while (pc < end) {
#    if __arm__
    again:
	if (data_info) {
	    while (data_info[data_offset] < pc) {
		data_offset += 2;
		if (data_offset >= _jit->data_info->length) {
		    data_info = null;
		    goto again;
		}
	    }
	    if (pc == data_info[data_offset]) {
		line = data_info[data_offset + 1];
		for (; line >= 4; line -= 4, pc += 4) {
		    bytes = sprintf(buffer, address_buffer_format, pc);
		    (*disasm_info.fprintf_func)(disasm_stream,
						"%*c0x%s\t.data\t0x%08x\n",
						16 - bytes, ' ', buffer,
						*(euint32_t *)(euint32_t)pc);
		}
		/* reset disassemble information instead of attempting
		 * to hack the arm specific backend data structures to
		 * tell it to forward the required number of bytes. */
		disasm_info.buffer = (eobject_t)(euint32_t)pc;
		disasm_info.buffer_vma = (euword_t)pc;
		if ((disasm_info.buffer_length = end - pc) <= 0)
		    break;
	    }
	}
#    endif
	if (eget_note((eint8_t *)(eword_t)pc, null, &name, &line) && name &&
	    (name != old_name || line != old_line)) {
	    (*disasm_info.fprintf_func)(disasm_stream, "# %*s:%d\n",
					name->length, name->v.u8, line);
	    old_name = name;
	    old_line = line;
	}
	bytes = sprintf(buffer, address_buffer_format, (eint_t)pc);
	(*disasm_info.fprintf_func)(disasm_stream, "%*c0x%s\t",
				    16 - bytes, ' ', buffer);
	pc += (*disasm_print)(pc, &disasm_info);
	putc('\n', disasm_stream);
    }
    fflush(disasm_stream);
}
#  endif	/* DISASSEMBLER */

#else
void
edisassemble(eobject_t bytecode, eint32_t length)
{
    eint32_t	*code;
    eint32_t	 count;
    eint32_t	 extra;
    eint32_t	*limit;
    eformat_t	 format;

    memset(&format, 0, sizeof(eformat_t));
    format.radix = 10;

    code = bytecode;
    limit = code + length;
    while (code < limit) {
	format.width = 8;
	eprint_int(std_output, &format, (code - (eint32_t *)bytecode) * 4);
	format.width = 0;
	dputc('\t');
	assert(*code >= eop_noop && *code < sizeof(codes) / sizeof(codes[0]));
	dputs(codes[*code].name, codes[*code].size);
	switch (*code++) {
	    case eop_noop:			case eop_nil:
		goto no_arg;
	    case eop_int:
		goto i_arg;
	    case eop_long:
		goto l_arg;
	    case eop_float:
		goto f_arg;
	    case eop_ll:			case eop_ld:
		goto i_arg;
	    case eop_ldt:
		goto i_i_arg;
	    case eop_lb:
		goto i_arg;
	    case eop_lbt:
		goto i_i_arg;
	    case eop_ls:
		goto i_arg;
	    case eop_lh:
		goto no_arg;
	    case eop_lhref:
		goto i_arg;
	    case eop_lv:
		goto i_arg;
	    case eop_lvi:
		goto i_arg;
	    case eop_lvref:
		goto i_i_arg;
	    case eop_lr:
		goto i_i_i_arg;
	    case eop_lrref:
		goto i_i_i_i_arg;
	    case eop_lc:
		goto i_i_arg;
	    case eop_lref:
		goto no_arg;
	    case eop_pd:			case eop_pdt:
		goto i_arg;
	    case eop_pb:			case eop_pbt:
		goto i_arg;
	    case eop_pv:			case eop_pvi:
		goto i_arg;
	    case eop_pr:
		goto i_i_i_arg;
	    case eop_pp:
		goto no_arg;
	    case eop_anon:
		goto i_i_arg;
	    case eop_sd:
		goto i_arg;
	    case eop_sdt:
		goto i_i_arg;
	    case eop_sb:
		goto i_arg;
	    case eop_sbt:
		goto i_i_arg;
	    case eop_ss:
		goto i_arg;
	    case eop_sst:
		goto i_i_arg;
	    case eop_ssv:
		goto i_arg;
	    case eop_sh:
		goto no_arg;
	    case eop_shref:
		goto i_arg;
	    case eop_sv:			case eop_svi:
	    case eop_svref:
		goto i_arg;
	    case eop_sr:
		goto i_i_i_arg;
	    case eop_srref:
		goto i_arg;
	    case eop_sc:
		goto i_i_arg;
	    case eop_sref:
		goto no_arg;
	    case eop_jt:			case eop_jf:
	    case eop_j:				case eop_jh:
	    case eop_jv:
		goto i_arg;
	    case eop_enter:
		goto ix4_list_arg;
	    case eop_begin:			case eop_off:
		goto i_arg;
	    case eop_push:
		goto no_arg;
	    case eop_pusht:
		goto i_arg;
	    case eop_pushv:
	    case eop_pop:
		goto no_arg;
	    case eop_call:
		goto i_arg;
	    case eop_meth:			case eop_ecm:
	    case eop_blt:
		goto i_i_arg;
	    case eop_ret:
		goto no_arg;
	    case eop_rett:
		goto i_arg;
	    case eop_reti:
		goto i_arg;
	    case eop_retit:
		goto i_i_arg;
	    case eop_ne:			case eop_lt:
	    case eop_le:			case eop_eq:
	    case eop_ge:			case eop_gt:
	    case eop_and:			case eop_or:
	    case eop_xor:			case eop_mul2:
	    case eop_div2:			case eop_shl:
	    case eop_shr:			case eop_add:
	    case eop_sub:			case eop_mul:
	    case eop_div:			case eop_trunc2:
	    case eop_rem:			case eop_atan2:
	    case eop_pow:			case eop_hypot:
	    case eop_complex:			case eop_inc:
	    case eop_dec:			case eop_bool:
	    case eop_not:			case eop_com:
	    case eop_inv:			case eop_neg:
	    case eop_signbit:			case eop_signum:
	    case eop_rational:			case eop_integer_p:
	    case eop_rational_p:		case eop_float_p:
	    case eop_real_p:			case eop_complex_p:
	    case eop_number_p:			case eop_finite_p:
	    case eop_inf_p:			case eop_nan_p:
	    case eop_num:			case eop_den:
	    case eop_real:			case eop_imag:
	    case eop_arg:			case eop_conj:
	    case eop_floor:			case eop_trunc:
	    case eop_round:			case eop_ceil:
	    case eop_abs:			case eop_sqrt:
	    case eop_cbrt:			case eop_sin:
	    case eop_cos:			case eop_tan:
	    case eop_asin:			case eop_acos:
	    case eop_atan:			case eop_sinh:
	    case eop_cosh:			case eop_tanh:
	    case eop_asinh:			case eop_acosh:
	    case eop_atanh:			case eop_proj:
	    case eop_exp:			case eop_log:
	    case eop_log2:			case eop_log10:
		goto no_arg;
	    case eop_new:
		goto i_i_arg;
	    case eop_mv:
		goto i_arg;
	    case eop_vnew:
		goto i_i_arg;
	    case eop_vnewi:
		goto i_i_list_arg;
	    case eop_vmv:
		goto i_arg;
	    case eop_renew:
		goto no_arg;
	    case eop_renewi:
		goto i_arg;
	    case eop_fref:
		goto i_i_i_arg;
	    case eop_apply:
		goto i_arg;
	    case eop_aret:			case eop_szof:
	    case eop_szofva:			case eop_szofdim:
		goto no_arg;
	    case eop_szofdimi:
		goto i_arg;
	    case eop_tpof:			case eop_subtpof:
	    case eop_rankof:
		goto no_arg;
	    case eop_vas:			case eop_val:
		goto i_arg;
	    case eop_lva:
		goto i_arg;
	    case eop_lvai:
		goto i_i_arg;
	    case eop_sva:
		goto i_arg;
	    case eop_svai:
		goto i_i_arg;
	    case eop_pva:
		goto i_arg;
	    case eop_try:
		goto i_arg;
	    case eop_catch:
		goto no_arg;
	    case eop_throw:
		goto i_arg;
	    case eop_unwind:			case eop_exit:
		goto no_arg;
	    case eop_setup:
		goto i_i_arg;
	    case eop_intp_0:			case eop_intp_v:
	    case eop_intp_n1:			case eop_realp_v:
	    case eop_nump_0:			case eop_nump_v:
		goto no_arg;
	    default:
		abort();
	}
    no_arg:
	dputc('\n');
	continue;
    i_arg:
	dputc(' ');
	eprint_int(std_output, &format, *code++);
	dputc('\n');
	continue;
    i_i_arg:
	dputc(' ');
	eprint_int(std_output, &format, *code++);
	dputc(' ');
	eprint_int(std_output, &format, *code++);
	dputc('\n');
	continue;
    i_i_list_arg:
	dputc(' ');
	eprint_int(std_output, &format, *code++);
	dputc(' ');
	eprint_int(std_output, &format, *code++);
	count = *code++;
	eprint_int(std_output, &format, count);
	for (; count; --count) {
	    dputc(' ');
	    eprint_int(std_output, &format, *code++);
	}
	dputc('\n');
	continue;
    ix4_list_arg:
	dputc(' ');
	eprint_int(std_output, &format, *code++);
	dputc(' ');
	eprint_int(std_output, &format, *code++);
	count = *code++;
	dputc(' ');
	eprint_int(std_output, &format, count);
	extra = *code++;
	dputc(' ');
	eprint_int(std_output, &format, extra);
	for (count += extra; count; --count) {
	    dputc(' ');
	    eprint_int(std_output, &format, *code++);
	}
	dputc('\n');
	continue;
    i_i_i_arg:
	dputc(' ');
	eprint_int(std_output, &format, *code++);
	dputc(' ');
	eprint_int(std_output, &format, *code++);
	dputc(' ');
	eprint_int(std_output, &format, *code++);
	dputc('\n');
	continue;
    i_i_i_i_arg:
	dputc(' ');
	eprint_int(std_output, &format, *code++);
	dputc(' ');
	eprint_int(std_output, &format, *code++);
	dputc(' ');
	eprint_int(std_output, &format, *code++);
	dputc(' ');
	eprint_int(std_output, &format, *code++);
	dputc('\n');
	continue;
    l_arg:
	dputc(' ');
	eprint_int(std_output, &format, *(eint_t *)code);
	dputc('\n');
	code += 2;
	continue;
    f_arg:
	dputc(' ');
	eprint_flt(std_output, &format, *(efloat64_t *)code);
	dputc('\n');
	code += 2;
	continue;
    }
    eflush(std_output);
}
#endif
